---
title: 编译系统结构
date: '2023-04-01'
draft: false
tags: ['小程序', '工具']
summary: 小程序是一种全新的连接用户与服务的方式，它可以在微信内被便捷地获取和传播，同时具有出色的使用体验。
---

### 小程序

### 小程序发展史

### 小程序与普通网页开发的区别

> > 以下内容引用微信小程序文档【小程序简介 > 小程序与普通网页开发的区别】

网页开发渲染线程和脚本线程是互斥的，这也是为什么长时间的脚本运行可能会导致页面失去响应，而在小程序中，二者是分开的，分别运行在不同的线程中。网页开发者可以使用到各种浏览器暴露出来的 DOM API，进行 DOM 选中和操作。而如上文所述，小程序的逻辑层和渲染层是分开的，逻辑层运行在 JSCore 中，并没有一个完整浏览器对象，因而缺少相关的 DOM API 和 BOM API。这一区别导致了前端开发非常熟悉的一些库，例如 jQuery、 Zepto 等，在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同，所以一些 NPM 的包在小程序中也是无法运行的。

​ 网页开发者需要面对的环境是各式各样的浏览器，PC 端需要面对 IE、Chrome、QQ 浏览器等，在移动端需要面对 Safari、Chrome 以及 iOS、Android 系统中的各式 WebView 。而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端，以及用于辅助开发的小程序开发者工具，小程序中三大运行环境也是有所区别的，如下表所示。

| 运行环境         | 逻辑层         | 渲染层            |
| ---------------- | -------------- | ----------------- |
| iOS              | javascriptCore | WKWebView         |
| 安卓             | V8             | chromium 定制内核 |
| 小程序开发者工具 | NWJS           | Chrome WebView    |

## 开发的准备流程

<img src="site/640.webp" alt="小程序注册&发布流程一览" />

---

<img src="site/641.png" />

### 申请帐号

- 邮箱注册
- 主体类型： 个人、企业、企业（个体工商户）、政府、媒体、其他组织
- ​ 除个体工商户类型可认证 5 个小程序外，其他类型一个主体可认证 50 个小程序

## 基础语法

### 配置

- [全局配置](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html)

```javascript
{
  "pages": [              // 页面路径列表
  "pages/index/index",
 ],
 "entryPagePath": "pages/index/index", // 小程序默认启动首页
  "window": {              // 全局的默认窗口表现
    "backgroundColor": "#F6F6F6",
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#F6F6F6",
    "navigationBarTitleText": "前端小书童",
    "navigationBarTextStyle": "black"
  },
 "tabBar": {              // 底部 tab 栏的表现
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页"
      },
      {
        "pagePath": "pages/logs/logs",
        "text": "我的"
      }
    ]
  },
  "sitemapLocation": "sitemap.json",
  "style": "v2",
  "lazyCodeLoading": "requiredComponents",
  "networkTimeout": {           // 网络超时时间
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true,              // 是否开启 debug 模式，默认关闭
  "functionalPages": false,         // 是否启用插件功能页，默认关闭
 "requiredBackgroundModes": ["audio", "location"] // 申明需要后台运行的能力，类型为数组 audio: 后台音乐播放 location: 后台定位
 "plugins":{},        // 使用到的插件
 "resizable": true
}
```

- [页面配置](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html)

```javascript
{
  "navigationBarBackgroundColor": "#ffffff",   // 导航栏背景颜色
  "navigationBarTextStyle": "black",       // 导航栏标题颜色
  "navigationBarTitleText": "前端小书童",     // 导航栏标题文字内容
  "navigationStyle": "default",          // 导航栏样式，仅支持以下值：default 默认样式;custom 自定义导航栏，只保留右上角胶囊按钮
  "homeButton": true,               // 在非首页、非页面栈最底层页面或非 tabbar 内页面中的导航栏展示 home 键
  "backgroundColor": "#eeeeee",         //  窗口的背景色
  "backgroundTextStyle": "light",        // 下拉 loading 的样式，仅支持 dark / light
  "enablePullDownRefresh": true,         // 是否开启当前页面下拉刷新
}
```

### WXML(模板)

WXML（WeiXin Markup Language）是框架设计的一套标签语言，结合基础组件、事件系统，可以构建出页面的结构。

#### 对比 Vue 模板语法

- WXML 数据绑定

```xml
<view> {{message}} </view>
```

- Vue 数据绑定

```xml
<div>{{message}}</div>
```

:::tip
WXML 不支持 html 标签（div、span 等），对于使用 view、block、text
:::

---

- WXML 循环渲染

```xml
<view wx:for="{{array}}" wx:for-index="index" wx:for-item="item" wx:key="index">
  {{index}}: {{item.message}}
</view>
```

- Vue 循环渲染

```xml
<div v-for="(item,index) in array" :key="index">
 {{index}}: {{item.message}}
</div>
```

---

- WXML 条件渲染

```xml
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
```

- Vue 条件渲染

```xml
<div  v-if="length > 5">
 1
</div>
<div  v-if="length > 2">
 2
</div>
<div v-else>3</div>
```

#### 组件(模板)-引用

定义组件(模板)

```xml
<!-- child.wxml -->
<template name="child">
  <text>{{text}}</text>
</template>
```

在另外一个模板文件中引用该组件(模板)

```xml
<!-- parent.wxml -->
<import src="child.wxml"/>
<template is="child" data="{{text: 'forbar'}}"/>
```

:::tip
在开发中组件的引入注册使用在页面的 json 文件中通过 usingComponents 引入会更易于维护
:::

### WXS(脚本)

WXS（WeiXin Script）是小程序的一套脚本语言，结合 WXML，可以构建出页面的结构。

WXS 与 javascript 是不同的语言，有自己的语法，并不和 javascript 一致。

通过以下方式引入 WXML 文件中

```xml
<wxs src="./demo.wxs" module="demoWxs"></wxs>
```

### 微信小程序全局事件订阅

在Vue开发中，我们可能用过eventBus来解决全局范围内的事件订阅及触发逻辑，在微信小程序的开发中我们可能也也会遇到同样的需求，那么我们尝试下在小程序（原生小程序开发）中实现类似eventBus的事件订阅功能。

#### 全局事件订阅

1. 全局实例
   在Vue中我们有new Vue得到的全局对象，小程序中对应的则是app对象，在小程序组件或者页面中可以通过getApp()获取；
2. 事件订阅
   声明对象存储事件，示例中使用map存储eventMap，向存储器中存放需要被触发的事件

```js
// 注意 开发阶段热跟新时，eventMap的声明和触发可能存在异步问题，需要阻断eventMap声明在触发之后的情况，这个问题仅限开发阶段存在
on(action, event) {
 if (eventMap && !eventMap.has(action)) {
  eventMap.set(action, event)
 }
  }
```

3. 事件触发
   当业务逻辑需要触发时，调用emit触发指定事件

```js
emit(action, arg) {
 if (eventMap && eventMap.has(action)) {
  eventMap.get(action) && eventMap.get(action)(arg)
 }
}
```

4. 事件卸载
   当订阅的事件过多或者确定事件不在被触发时，及时卸载事件可以减少内存压力

```js
off(action) {
 if (eventMap && eventMap.has(action)) {
  eventMap.delete(action)
 }
   }
```

整体代码如下（文件：app.js）：

```js
const eventMap = new Map()
App({
  globalData: {
    count: 1,
  },
  // 事件订阅
  on(action, event) {
    if (eventMap && !eventMap.has(action)) {
      eventMap.set(action, event)
    }
  },
  // 事件卸载
  off(action) {
    if (eventMap && eventMap.has(action)) {
      eventMap.delete(action)
    }
  },
  // 事件触发
  emit(action, arg) {
    if (eventMap && eventMap.has(action)) {
      eventMap.get(action) && eventMap.get(action)(arg)
    }
  },
})
```

##### 页面或者组件中使用

1. 订阅on，订阅自定义事件countAdd（自定义事件名），并且传入事件被触发后需要被触发的逻辑，这里的changeCount就是在事件被触发是订阅触发的数据，当然触发事件的参数可以来自emit也可以无参数

```js
const app = getApp()
Page({
  data: {
    count: app.globalData.count,
  },
  created() {
    // 注册事件
    app.on('countAdd', this.changeCount.bind(this))
  },
  changeCount(count) {
    this.setData({
      count,
    })
  },
})
```

2. 发布emit，发布自定义事件countAdd（自定义事件名）来触发所有监听该事件的订阅者（既注册了on的组件或者页面），emit携带的参数也会被传递给自定义事件

```js
const app = getApp()
Component({
  data: {
    count: app.globalData.count,
  },
  // 触发事件
  bindEvent() {
    app.emit('countAdd', this.data.count++)
  },
})
```

> 这里changeCount是最终被触发的事件，countAdd是在订阅服务中自定义的事件名，之所以不使用相同的事件名，主要是区分下。

#### 整体事件触发逻辑如下

1. 先订阅事件 changeCount
2. 业务需要触发的时候触发bindEvent
3. emit到全局来调用监听的事件
